home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / dstack.h < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  11.5 KB  |  309 lines

  1. /* Copyright (C) 1992, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: dstack.h,v 1.2 2000/09/19 19:00:09 lpd Exp $ */
  20. /* Definitions for the interpreter's dictionary stack */
  21.  
  22. #ifndef dstack_INCLUDED
  23. #  define dstack_INCLUDED
  24.  
  25. #include "idstack.h"
  26. #include "icstate.h"        /* for access to dict_stack */
  27.  
  28. /* Define the dictionary stack instance for operators. */
  29. #define idict_stack (i_ctx_p->dict_stack)
  30. #define d_stack (idict_stack.stack)
  31.  
  32. /* Define the interpreter-specific versions of the generic dstack API. */
  33. #define min_dstack_size (idict_stack.min_size)
  34. #define dstack_userdict_index (idict_stack.userdict_index)
  35. #define dsspace (idict_stack.def_space)
  36. #define dtop_can_store(pvalue) ((int)r_space(pvalue) <= dsspace)
  37. #define dtop_keys (idict_stack.top_keys)
  38. #define dtop_npairs (idict_stack.top_npairs)
  39. #define dtop_values (idict_stack.top_values)
  40. #define dict_set_top() dstack_set_top(&idict_stack);
  41. #define dict_is_permanent_on_dstack(pdict)\
  42.   dstack_dict_is_permanent(&idict_stack, pdict)
  43. #define dicts_gc_cleanup() dstack_gc_cleanup(&idict_stack)
  44. #define systemdict (&idict_stack.system_dict)
  45.  
  46. /* Define the dictionary stack pointers. */
  47. #define dsbot (d_stack.bot)
  48. #define dsp (d_stack.p)
  49. #define dstop (d_stack.top)
  50.  
  51. /* Macro to ensure enough room on the dictionary stack */
  52. #define check_dstack(n)\
  53.   if ( dstop - dsp < (n) )\
  54.     { d_stack.requested = (n); return_error(e_dictstackoverflow); }
  55.  
  56. /*
  57.  * The dictionary stack is implemented as a linked list of blocks;
  58.  * operators that access the entire d-stack must take this into account.
  59.  * These are:
  60.  *      countdictstack  dictstack
  61.  * In addition, name lookup requires searching the entire stack, not just
  62.  * the top block, and the underflow check for the dictionary stack
  63.  * (`end' operator) is not just a check for underflowing the top block.
  64.  */
  65.  
  66. /* Name lookup */
  67. #define dict_find_name_by_index(nidx)\
  68.   dstack_find_name_by_index(&idict_stack, nidx)
  69. #define dict_find_name(pnref) dict_find_name_by_index(name_index(pnref))
  70. #define dict_find_name_by_index_inline(nidx, htemp)\
  71.   dstack_find_name_by_index_inline(&idict_stack, nidx, htemp)
  72. #define if_dict_find_name_by_index_top(nidx, htemp, pvslot)\
  73.   if_dstack_find_name_by_index_top(&idict_stack, nidx, htemp, pvslot)
  74.  
  75. /*
  76. Notes on dictionary lookup performance
  77. --------------------------------------
  78.  
  79. We mark heavily used operations with a * below; moderately heavily used
  80. operations with a +.
  81.  
  82. The following operations change the dictionary stack:
  83.     +begin, +end
  84.     readonly (on a dictionary that is on the stack)
  85.     noaccess (on a dictionary that is on the stack)
  86.     context switch
  87. We implement cleardictstack as a series of ends.
  88.  
  89. The following operations change the contents of dictionaries:
  90.     *def, +put
  91.     undef
  92.     restore
  93.     .setmaxlength
  94. We implement store in PostScript, and copy as a series of puts.  Many
  95. other operators also do puts (e.g., ScaleMatrix in makefont,
  96. Implementation in makepattern, ...).  Note that put can do an implicit
  97. .setmaxlength (if it has to grow the dictionary).
  98.  
  99. The following operations look up keys on the dictionary stack:
  100.     *(interpreter name lookup)
  101.     load
  102.     where
  103.  
  104. Current design
  105. --------------
  106.  
  107. Each name N has a pointer N.V that has one of 3 states:
  108.     - This name has no definitions.
  109.     - This name has exactly one definition, in systemdict or userdict.
  110.     In this case, N.V points to the value slot.
  111.     - This name has some other status.
  112.  
  113. We cache some pointers to the top dictionary on the stack if it is a
  114. readable dictionary with packed keys, which allows us to do fast,
  115. single-probe lookups in this dictionary.  We also cache a value that
  116. allows us to do a fast check for stores into the top dictionary
  117. (writability + space check).
  118.  
  119. Cheap shallow binding
  120. ---------------------
  121.  
  122. We define a global event counter, Event.  Each name N has, in addition to
  123. its value pointer N.V, an associated event stamp N.E.  The invariant we want
  124. to preserve is that N.V is valid iff N.E >= Event.  Similarly, each entry B
  125. on the dictionary stack has, in addition to its dictionary B.D, an
  126. associated event stamp B.E which is the value of Event when the entry was
  127. made, and a slow lookup flag B.F which indicates whether any slow lookups
  128. have been done since the entry was made.  Finally, each dictionary D has a
  129. counter D.N which indicates how many times it appears on the dictionary
  130. stack.  (The counter is an optional feature of this scheme: if we omit it,
  131. we consider it to have a permanently non-zero value in all dictionaries.)
  132.  
  133. The idea of the B.F flag is for 'end' to be able to reset Event, rather than
  134. incrementing it, if no names had their stamp set to the incremented Event
  135. value.  This in turn prevents the mass invalidation of the N.V cache that
  136. would otherwise occur.
  137.  
  138. Here are the implementations of the various operations with respect to the
  139. cache.  We preserve the current scheme for fast implementation of 'def',
  140. which we don't discuss here.
  141.  
  142. Initialize:
  143.     Event = 0
  144. When creating a name:
  145.     N.V = NULL, N.E = 0
  146. begin(D):
  147.     create B with B.D = D, B.E = Event, B.F = false
  148.     ++Event
  149. end(B):
  150.     if !B.F, Event = B.E; else ++Event, and set B.F in the new top entry
  151. readonly(D):
  152.     << nothing >>
  153. noaccess(D):
  154.     if D.N, ++Event
  155. def(D,K,V):
  156.     if K is already defined in D or K is not a name N, nothing to do
  157.     if N.V is NULL and D is systemdict,
  158.       set N.V to point into D, N.E = infinite
  159.     else
  160.       set N.V to point into D, N.E = Event, B.F = true in top entry
  161. put(D,K,V):
  162.     if K is already defined in D, K is not a name N, or D.N == 0,
  163.       nothing to do
  164.     if N.V is NULL and D is systemdict,
  165.       set N.V to point into D, N.E = infinite
  166.     else if D is the top dict on the dict stack
  167.       set N.V to point into D, N.E = Event, B.F = true in top entry
  168.     else
  169.       set N.V = invalid, N.E = 0
  170. undef(D,K):
  171.     if K is defined in D, D.N > 0, and K is a name N, clear N.E
  172. restore:
  173.     ****** TBC ******
  174. .setmaxlength(D):
  175.     for each name key N in D, if N.V points into D, repoint it
  176. value = lookup(N): (load and where are similar)
  177.     << do the fast lookup in the top dict >>
  178.     if N.E >= Event, use N.V
  179.     otherwise, do the dict stack lookup
  180.     set B.F in the top dict stack entry
  181.     set N.E = Event, N.V = the binding pointer
  182. context switch:
  183.     ++Event
  184.     for each B on the old dict stack, --(B.D.N)
  185.     for each B on the new dict stack, ++(B.D.N)
  186. Event counter overflow:
  187.     clear N.E in all names where it isn't infinite
  188.  
  189. Full shallow binding
  190. --------------------
  191.  
  192. We implement shallow binding with a pointer in each name that points to
  193. the value slot that holds the name's definition.  If the name is
  194. undefined, or if we don't know where the slot is, the binding pointer
  195. points to a ref with a special type t__invalid, which cannot occur
  196. anywhere else.  "Clearing" the pointer means setting it to point to this
  197. ref.
  198.  
  199. We also maintain a pair of pointers that bracket the value region of the
  200. top dictionary on the stack, for fast checking in def.  If the top
  201. dictionary is readonly or noaccess, the pointers designate an empty area.
  202. We call this the "def region" cache.
  203.  
  204. We implement the above operations as follows:
  205.     begin - push the dictionary on the stack; set the pointers of
  206.         all name keys to point to the corresponding value slots.
  207.     end - pop the stack; clear the pointers of all name keys.
  208.     readonly - if the dictionary is the top one on the stack,
  209.         reset the def region cache.
  210.     noaccess - clear the pointers of all name keys.  (This is overly
  211.         conservative, but this is a very rare operation.)
  212.         Also reset the def region cache if the dictionary is
  213.         the top one on the stack.
  214.     def - if the key is a name and its pointer points within the cached
  215.         def region, store the value through the pointer; otherwise,
  216.         look up the key in the top dictionary, store the value,
  217.         and if the key is a name, set its pointer to the value slot.
  218.     put - if the key is a name and wasn't in the dictionary before,
  219.         clear its pointer.  (Conservative, but rare.)
  220.     undef - if the key is a name, clear its pointer.  (Overly
  221.         conservative, but rare.)
  222.     restore - if either the old or the new value of a change is a name
  223.         (possibly in a packed array), clear its pointer.  This is
  224.         conservative, but easy to detect, and probably not *too*
  225.         conservative.
  226.     .setmaxlength - clear all the pointers, like noaccess.
  227.     (name lookup) - fetch the value through the pointer and dispatch
  228.         on its type; if the type is t__invalid, do a full search
  229.         and set the pointer.  This avoids a separate check for a
  230.         clear pointer in the usual case where the pointer is valid.
  231.     load - if the pointer is clear, do a search and set the pointer;
  232.         then fetch the value.
  233.     where - always do a full search and set the pointer.
  234.         (Conservative, but rare.)
  235.  
  236. One place where shallow binding will result in major new overhead is the
  237. extra push of systemdict for loading fonts.  This probably isn't a problem
  238. in real life.
  239.  
  240. ****** Context switching is horrendously expensive: it has to do the
  241. equivalent of 'end' for every dictionary on the old stack followed by
  242. 'begin' for every dictionary on the new stack.
  243.  
  244. Adaptive shallow binding
  245. ------------------------
  246.  
  247. We do validity checking for the name value cache using an epoch counter.
  248. For each dictionary D, we keep an on-stack flag F.  Each dictionary stack
  249. entry is <D,M,F,E> where D is the actual dictionary, M is a mark vector of
  250. V bits (V is a system constant, probably 64), F is D's former on-stack
  251. flag, and E is the epoch at which the entry was made.  For each name K, we
  252. keep a cache <P,E> where P is a pointer to the dictionary value slot that
  253. holds the current value of K, and E is an epoch value; the cache is valid
  254. if K->E >= dsp->E.  Here is what happens for each operation:
  255.  
  256. ****** Still need to handle names defined only in systemdict or userdict?
  257.  
  258. To initialize:
  259.     Epoch = 0
  260. To clear the cache entry for K:
  261.     *K = <ptr to invalid value, 0>
  262. begin(D):
  263.     *++dsp = <D, {0...}, D->F, ++Epoch>
  264.     set D->F
  265. value = lookup(K):
  266.     if K->E >= dsp->E
  267.       value = *K->P
  268.     else
  269.       do lookup as usual
  270.       *K = <ptr to value, Epoch>
  271.       set dp->M[i mod V] where dp is the dstack slot of the dictionary
  272.         where K was found and i is the index within that dictionary
  273. end:
  274.     for each i such that dsp->M[i] is set,
  275.       clear the cache entry for dsp->D->keys[i, i+V, ...]
  276.     dsp->D->F = dsp->F
  277.     --dsp
  278. noaccess(D):
  279.     if D->F is set,
  280.       clear the cache entries for all name keys of D
  281. readonly(D):
  282.     << nothing >>
  283. .setmaxlength(D,N):
  284.     same as noaccess
  285. restore:
  286.     If either the old or the new value of a change is a name
  287.       (possibly in a packed array), clear its cache entry.  This is
  288.       conservative, but easy to detect, and probably not *too*
  289.       conservative.
  290. def(K,V):
  291.     if K->P points into dsp->D
  292.       *K->P = V
  293.     else
  294.       put the new value in dsp->D
  295.       set *K and dsp->M[i mod V] as for a lookup
  296. put(D,K,V):
  297.     if K is already defined in D, do nothing special
  298.     otherwise, if D->F isn't set, do nothing special
  299.     otherwise, clear K's cache entry
  300. undef(D,K):
  301.     if D->F is set,
  302.       clear K's cache entry
  303.  
  304. ****** Same problem for context switching as for full shallow binding.
  305.  
  306.  */
  307.  
  308. #endif /* dstack_INCLUDED */
  309.